﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Diagnostics;

namespace Oliver.Controls
{
    
    [Description("A carousel panel for displaying elements")]
    public class CarouselPanel : BaseContainer 
    {
        public CarouselPanel( ) 
            : base () 
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }

            this.MouseMove += new MouseEventHandler(CarouselPanel_MouseMove);
            this.MouseEnter += new MouseEventHandler(CarouselPanel_MouseEnter);
            this.MouseLeave += new MouseEventHandler(CarouselPanel_MouseLeave);

            this.Loaded += new RoutedEventHandler(CarouselPanel_Loaded);
        }

        protected DispatcherTimer _timer;

        #region Internal EventHandler

        void CarouselPanel_Loaded(object sender, RoutedEventArgs e)
        {
            if (AutoStart)
            {
                StartCarousel();
            }
        }

        void CarouselPanel_MouseLeave(object sender, MouseEventArgs e)
        {
            if (OnlyRunningWhenMouseOver)
            {
                StopCarousel();
            }
        }

        void CarouselPanel_MouseEnter(object sender, MouseEventArgs e)
        {
            if (OnlyRunningWhenMouseOver)
            {
                StartCarousel();
            }
        }
        
        void _timer_Tick(object sender, EventArgs e)
        {
            DoArrange();
        }

        void CarouselPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (UseMousePosition)
            {
                Point p = e.GetPosition(this);
                double diffX = (this.ActualWidth / 2) - p.X;
                if (Math.Abs(diffX) > (this.ItemWidth / 2))
                {
                    _internalSpeed = -1 * Speed * (diffX / 100);
                }
                else
                {
                    _internalSpeed = 0d;
                }
            }
        }

        #endregion

        #region Internal overrides

        protected override Size ArrangeOverride(System.Windows.Size finalSize)
        {
            for (int i = 0; i < Children.Count; i++)
            {
                // Calculate and set angle of all children
                double angle = i * (Math.PI * 2) / Children.Count;
                Children[i].SetValue(CarouselPanel.AngleProperty, angle);
            }
            DoArrange();

            return base.ArrangeOverride(finalSize);
        }

        Size _size;

        protected override Size MeasureOverride(Size availableSize)
        {
            _size = availableSize;
            if (ResizeItems)
            {
                foreach (UIElement element in Children)
                {
                    element.Measure(new Size(ItemWidth, ItemHeight));
                }
            }
            foreach (UIElement element in Children)
            {
                element.MouseLeftButtonDown += element_MouseLeftButtonDown;
            }
            _highestZindex = 0;
            return availableSize;
        }

        void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            OnElementClicked(sender);
        }

        private void OnElementClicked(object sender)
        {
            if (ElementClicked != null)
            {
                ElementClicked(sender, EventArgs.Empty);
            }
        }

        public event EventHandler ElementClicked;

        #endregion

        #region Properties

        UIElement _selectedUiElement;
        public UIElement SelectedUiElement
        {
            get { return _selectedUiElement; }
            set { _selectedUiElement = value; }
        }

        private bool _autoStart = true;
        [Description("Carousel starts rotating after loading, if value is set to true")]
        public bool AutoStart
        {
            get { return _autoStart; }
            set { _autoStart = value; }
        }

        private bool _onlyRunningWhenMouseOver = false;
        [Description("Stops when Mouse leaves the control, and starts when mouse is over the control")]
        public bool OnlyRunningWhenMouseOver
        {
            get { return _onlyRunningWhenMouseOver; }
            set { _onlyRunningWhenMouseOver = value; }
        }

        private bool _resizeItems = false;
        public bool ResizeItems
        {
            get { return _resizeItems; }
            set { _resizeItems = value; }
        }

        public double ItemWidth
        {
            get { return (double)GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }

        public static readonly DependencyProperty ItemWidthProperty =
            DependencyProperty.Register("ItemWidth", typeof(double), typeof(BaseContainer),
            new PropertyMetadata(100.0, OnItemWidthChanged));

        private static void OnItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaseContainer baseContainer = d as BaseContainer;
            baseContainer.InvalidateArrange();
        }

        public double ItemHeight
        {
            get { return (double)GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }

        public static readonly DependencyProperty ItemHeightProperty =
            DependencyProperty.Register("ItemHeight", typeof(double), typeof(BaseContainer),
            new PropertyMetadata(100.0, OnItemHeightChanged));

        private static void OnItemHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaseContainer baseContainer = d as BaseContainer;
            baseContainer.InvalidateArrange();
        }

        private double _highlightScale = 2;
        public double HighlightScale
        {
            get { return _highlightScale; }
            set { _highlightScale = value; }
        }

        int _highestZindex = 0;
        private double _internalSpeed = 0.2;
        private double _internalSpeedDefault = 0.2;
        private double _internalSpeedFast = 1;

        public double Speed
        {
            get { return (double)GetValue(SpeedProperty); }
            set 
            { 
                SetValue(SpeedProperty, value);
                this.InvalidateArrange(); 
            }
        }

        public static readonly DependencyProperty SpeedProperty =
                DependencyProperty.Register("Speed", typeof(double), 
                typeof(CarouselPanel), 
                new PropertyMetadata (1.0, 
                new PropertyChangedCallback(OnSpeedChanged) ));

        protected static void OnSpeedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            CarouselPanel panel = obj as CarouselPanel;
            panel._internalSpeed = (double)args.NewValue;
        }

        public bool UseMousePosition
        {
            get { return (bool)GetValue(UseMousePositionProperty); }
            set { SetValue(UseMousePositionProperty, value); this.InvalidateArrange(); } 
        } 
   
        public static readonly DependencyProperty UseMousePositionProperty =
                DependencyProperty.Register("UseMousePosition"
                , typeof(bool), typeof(CarouselPanel), new PropertyMetadata(true));

        public double ScalePerspective
        {
            get
            {
                double val = (double)GetValue(ScalePerspectiveProperty);
                return val;
            }
            set { SetValue(ScalePerspectiveProperty, value); this.InvalidateArrange(); }
        }

        public static readonly DependencyProperty ScalePerspectiveProperty =
                DependencyProperty.Register("ScalePerspective", typeof(double), 
                typeof(CarouselPanel), 
                new PropertyMetadata ( 0.3,
                new PropertyChangedCallback(OnScalePerspectiveChanged)));

        protected static void OnScalePerspectiveChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
        {
            CarouselPanel panel = obj as CarouselPanel; 
            if (panel != null) 
            { 
                panel.InvalidateArrange(); 
            }
        }

        private int _secondsToWaitUntilContinue = 3;
        public int SecondsToWaitUntilContinue
        {
            get { return _secondsToWaitUntilContinue; }
            set { _secondsToWaitUntilContinue = value; }
        }

        public static readonly DependencyProperty AngleProperty =
           DependencyProperty.RegisterAttached("Angle", typeof(double), typeof(CarouselPanel), null);
        
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set
            {
                SetValue(AngleProperty, value);
                this.InvalidateArrange(); 
            }
        }

        private bool _isRunning;
        public bool IsRunning
        {
            get { return _isRunning; }
        }

        #endregion

        public event EventHandler ElementHighlighted;

        private void StopCarousel()
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Tick -= _timer_Tick;
            }
            _isRunning = false;
        }

        private void StartCarousel()
        {
            StopCarousel();

            _internalSpeed = _internalSpeedDefault;
            //if (_timer == null)
            //{
                _timer = new DispatcherTimer();
                _timer.Tick += _timer_Tick;
            //}
            _isRunning = true;
            //_timer.Interval = new TimeSpan(0, 0, 0, 0, MillisecondPerFrame);;
            _timer.Interval = FrameRate;
            _timer.Start();
        }

        void DoArrange()
        {
            double height = this.DesiredSize.Height;
            double width = this.DesiredSize.Width;

            Point center = new Point((width - ItemWidth) / 2, (height - ItemHeight) / 2);
            double radiusX = center.X;
            double radiusY = center.Y; 
            double scale = ScalePerspective;

            for (int i = 0; i < Children.Count; i++)
            {
                UIElement item = Children[i];
                
                double current = (double)item.GetValue(CarouselPanel.AngleProperty);
                
                double angle = current + (_internalSpeed * (3 / 360.0) * (2 * Math.PI));
                item.SetValue(CarouselPanel.AngleProperty, angle);
                
                Point p = new Point(
                    (Math.Cos(angle) * radiusX) + center.X,
                    (Math.Sin(angle) * radiusY) + center.Y);

                // Scale
                if (item.RenderTransform == null || 
                    (!(item.RenderTransform is ScaleTransform)))
                {
                    item.RenderTransform = new ScaleTransform();
                }
                ScaleTransform sc = item.RenderTransform as ScaleTransform;
                double scaleMinusRounding  = p.Y / (center.Y + radiusY); 
                sc.ScaleX = sc.ScaleY =  Math.Min(scaleMinusRounding + scale, 1.0) ;

                // Set Z-Index 
                int zIndex = (int)((p.Y / _size.Height) * 1000);
                item.SetValue(Canvas.ZIndexProperty, zIndex);

                _highestZindex = zIndex > _highestZindex ? zIndex : _highestZindex;

                //if (ResizeItems)
                //{
                    Rect r = new Rect(p.X, p.Y, ItemWidth, ItemHeight);
                    item.Arrange(r);
                //}
                //else
                //{
                //    FrameworkElement fe = item as FrameworkElement;
                //    Rect r = new Rect(p.X, p.Y, fe.Width, fe.Height);
                //}
                
                item.InvalidateArrange();
            }

            UIElement highest = null;
            foreach (var i in this.Children)
            {
                if (highest == null)
                {
                    highest = i;
                }
                else
                {
                    if ((int)i.GetValue(Canvas.ZIndexProperty) > (int)highest.GetValue(Canvas.ZIndexProperty))
                    {
                        highest = i;
                    }
                }
            }

            if (_selectedUiElement != null && highest != null && _selectedUiElement == highest)
            {
                int zindex = (int)highest.GetValue(Canvas.ZIndexProperty);
                Debug.WriteLine(zindex);

                if (zindex >= _highestZindex)
                {
                    StopCarousel();
                    Highlight(_selectedUiElement);
                    _internalSpeed = _internalSpeedDefault;
                }
            }
        }

        private void Highlight(UIElement e)
        {
            TransformGroup tg = new TransformGroup();
            
            ScaleTransform st = new ScaleTransform();
            st.ScaleX = _highlightScale;
            st.ScaleY = _highlightScale;
            tg.Children.Add(st);

            TranslateTransform tt = new TranslateTransform();
            tt.X = -(e as FrameworkElement).Width / 2;
            tt.Y = -(e as FrameworkElement).Height / 2;
            tg.Children.Add(tt);

            e.RenderTransform = tg;
            OnElementHighlighted(e);
        }
        
        private void OnElementHighlighted(UIElement e)
        {
            if (ElementHighlighted != null)
            {
                ElementHighlighted(e, EventArgs.Empty);
            }
            _selectedUiElement = null;
            StartWaitTimer();
        }

        private void StartWaitTimer()
        {
            _waitTimer = new DispatcherTimer();
            _waitTimer.Interval = new TimeSpan(0, 0, 0, _secondsToWaitUntilContinue);
            _waitTimer.Tick += _waitTimer_Tick;
            _waitTimer.Start();
        }

        void _waitTimer_Tick(object sender, EventArgs e)
        {
            if (_waitTimer != null)
            {
                _waitTimer.Stop();
                _waitTimer.Tick -= _waitTimer_Tick;
                _waitTimer = null;
            }
            StartCarousel();
        }
        
        public void BringToFront(object sender)
        {
            _selectedUiElement = sender as UIElement;    
            Debug.WriteLine(sender.ToString().ToString());
            _internalSpeed = _internalSpeedFast;

            if (!_isRunning)
            {
                StartCarousel();
            }
        }

        DispatcherTimer _waitTimer;
    }
}
